package com.yanyeori.framework.core.util;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.cfg.ConfigFeature;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * jackson的简单封装工具类
 *
 * @author chenkuan 2022/10/28
 */
public class JacksonUtil implements InitializingBean {

    private static volatile ObjectMapper OBJECT_MAPPER;

    private static final Map<ConfigFeature, Boolean> FEATURE_DEFAULTS;

    private final Jackson2ObjectMapperBuilder builder;

    static {
        FEATURE_DEFAULTS = new HashMap<>();
        FEATURE_DEFAULTS.put(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        FEATURE_DEFAULTS.put(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true);
    }

    public JacksonUtil(Jackson2ObjectMapperBuilder builder) {
        this.builder = builder;
    }

    @Override
    public void afterPropertiesSet() {
        OBJECT_MAPPER = builder.createXmlMapper(false).build();
        configure(OBJECT_MAPPER, FEATURE_DEFAULTS);
    }

    public static ObjectMapper objectMapper() {
        if (OBJECT_MAPPER == null) {
            synchronized (JacksonUtil.class) {
                if (OBJECT_MAPPER == null) {
                    OBJECT_MAPPER = new ObjectMapper();
                    configure(OBJECT_MAPPER, FEATURE_DEFAULTS);
                }
            }
        }
        return OBJECT_MAPPER;
    }

    private static void configure(ObjectMapper objectMapper, Map<ConfigFeature, Boolean> featureDefaults) {
        featureDefaults.forEach((feature, state) -> {
            if (feature instanceof SerializationFeature) {
                objectMapper.configure((SerializationFeature) feature, state);
            } else if (feature instanceof DeserializationFeature) {
                objectMapper.configure((DeserializationFeature) feature, state);
            } else if (feature instanceof MapperFeature) {
                objectMapper.configure((MapperFeature) feature, state);
            }
        });
        objectMapper.registerModule(new JavaTimeModule());
    }

    public static String toJson(Object value) {
        try {
            return objectMapper().writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static String toJsonPretty(Object value) {
        try {
            return objectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static JsonNode parseNode(String content) {
        try {
            return objectMapper().readTree(content);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static ObjectNode parseObject(String content) {
        return parseNode(content).deepCopy();
    }

    public static <T> T parseObject(String content, Class<T> valueType) {
        try {
            return objectMapper().readValue(content, valueType);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> T parseObject(String content, TypeReference<T> valueTypeRef) {
        try {
            return objectMapper().readValue(content, valueTypeRef);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> T parseObject(String content, JavaType valueType) {
        try {
            return objectMapper().readValue(content, valueType);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static ArrayNode parseArray(String content) {
        return parseNode(content).deepCopy();
    }

    public static <T> List<T> parseArray(String content, Class<T> valueType) {
        try {
            return objectMapper().readValue(content, constructParametricType(ArrayList.class, valueType));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static JavaType constructParametricType(Class<?> parametrized, Class<?>... parameterClasses) {
        return objectMapper().getTypeFactory().constructParametricType(parametrized, parameterClasses);
    }
}
